package com.dt.shirodemo.config;


import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

//标记当前类是一个 spring 的配置类，用于模拟Spring 的配置文件
//在这里配置Shiro
@Configuration
public class ShiroConfig {

    @Bean
    public SecurityManager securityManager(Realm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //设置一个Realm,这个 Realm 是最终用于完成我们认证号和授权操作的具体对象
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    //配置一个自定义的Realm 的bean ,最终将使用这个bean返回的对象来完成我们的认证和授权
    @Bean
    public Realm myRealm() {
        MyRealm realm = new MyRealm();
        return realm;
    }

    //配置一个Shiro 的过滤器bean，这个bean 将配置Shiro 相关的一个规则的拦截
    //例如什么样的请求可以访问，什么样的请求不可以访问等等
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        //创建Shiro 的拦截的拦截器，用于拦截我们的用户请求
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置Shiro 的安全管理，设置管理的同时也会指定某个Realm 用来完成我们权限分配
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //用于设置一个登录的请求地址，这个地址可以是一个html或jsp的访问路径，也可以是一个控制器的路径
        //作用是用于通知 Shiro 我们可以使用这里的路径转向登陆页面，但Shiro判断到我们当前的用户没有登陆时就会自动转换到这个路径
        //要求用户完成成功
        shiroFilterFactoryBean.setLoginUrl("/login");
        //登陆成功后的返回界面，由于用户的登录后，需要交给Shiro完成，因此就需要通知 Shiro 登录成功之后返回到那个位置
        shiroFilterFactoryBean.setSuccessUrl("/success");
        //用于指定没有权限的界面，当用户访问某个功能时，如果Shiro 判断这个用户没有对应的操作权限，那么Shiro就会将请求转向这个位置，用于提示用户没有操作权限
        shiroFilterFactoryBean.setUnauthorizedUrl("/noPermission");
        //定义一个Map集合，这个Map集合中存放的数据全部都是规则，用于设置通知Shiro什么样的请求可以访问什么样的请求不可以访问
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        // login 表示某个请求的名字，anon 表示可以使用游客进行访问（不需要登陆）
        // map.put("/login","anon");

        //表示所有的请求路径全部需要被拦截登录，这个必须写在 Map 集合的最后面，这个选项是可选的
        //如果没有指定/** 那么如果某个请求不符合上面的拦截规则 Shiro 将放行这个请求
        // map.put("/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    /**
     * 开启 Shiro 的注解例如（@RequiresRoles @RequiresUser @RequiresPermissions）
     * 需要借助springAOP 来扫描这些注解
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 开启AOP 的注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor
    authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
                = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    //配置Shiro标签：
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /*
    注意：启动注解的权限控制以后需要删除在 Shiro 配置类中的权限拦截的配置规则
        map.put("/admin/test","authc,perms[admin:add]");
        map.put("/admin/**","authc,roles[admin]");
        map.put("/user/**","authc,roles[user]");

     */
}
